﻿using CleanArchitecture.Core.Events;
using CleanArchitecture.Core.Interfaces;
using CleanArchitecture.Core.Interfaces.Cache;
using CleanArchitecture.Infrastructure.Configuration;
using CleanArchitecture.Infrastructure.Entities.AdminUsers;
using CleanArchitecture.Infrastructure.Entities.SystemModules;
using CleanArchitecture.Infrastructure.Interfaces;
using CleanArchitecture.Infrastructure.Interfaces.SystemManage;
using CleanArchitecture.Infrastructure.Services.DatabaseProvider;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using static CleanArchitecture.Infrastructure.Entities.EntityEnumConfig;

namespace CleanArchitecture.Infrastructure.Services.SystemManage
{
    public partial class SystemModuleSevice : ISystemModuleService
    {
        #region Fields
        private readonly IRepository<SystemModule> _sysmoduleRepo;
        private readonly IRepository<AdminRoleSystemModuleMapping> _adminrolesystemmoduleReposity;
        private readonly IStaticCacheManager _cacheManager;
        private readonly IDomainEventDispatcher _eventDispatcher;
        private readonly IDataProvider _dataProvider;
        private readonly IDbContext _dbContext;
        #endregion

        #region Ctor
        public SystemModuleSevice(IRepository<SystemModule> sysmoduleRepo,
            IRepository<AdminRoleSystemModuleMapping> adminrolesystemmoduleRepo,
            IStaticCacheManager cacheManager,
            IDomainEventDispatcher eventDispatcher,
            IDataProvider dataProvider,
            IDbContext dbContext)
        {
            this._sysmoduleRepo = sysmoduleRepo;
            this._adminrolesystemmoduleReposity = adminrolesystemmoduleRepo;
            this._cacheManager = cacheManager;
            this._eventDispatcher = eventDispatcher;
            this._dataProvider = dataProvider;
            this._dbContext = dbContext;
        }
        #endregion

        #region Method
        public virtual IList<SystemModule> GetSystemMenusAll()
        {
            Func<IList<SystemModule>> LoadSystemModuleFunc = delegate () {
                var query = _sysmoduleRepo.TableNoTracking;
                query = query.Where(module => module.IsActive == YesorNo.YES);
                query = query.OrderBy(module => module.ParentId).ThenBy(module => module.DisplayOrder);
                return query.ToList();
            };
            
            var systemMenus = _cacheManager.Get(DefaultCacheConfig.SystemNaviMenu, () =>
            {
                var result = new List<SystemModule>();
                foreach (var menu in LoadSystemModuleFunc())
                    result.Add(new MenuForCaching(menu));
                return result;
            });

            return systemMenus;
        }

        public virtual IList<SystemModule> GetSystemMenuByUser(AdminUser user)
        {
            if (user == null) return null;
            var userRoles = user.GetAdminRoles();
            /***
                * SELECT DISTINCT mo.Id, mo.ModuleName, mo.ModuleType, mo.PID, mo.[Order]  
                * FROM [dbo].adminrole_sysmodule_mapping as mp
                *  INNER JOIN dbo.sys_module as mo 
                *  ON mp.FK_SysModule_Id = mo.Id 
                *  where mp.FK_AdminRole_Id in ( SELECT Id from dbo.admin_role where id in (2,3)) 
                *  ORDER BY mo.PID, mo.[Order] 
                ***/
            Func<int[], IList<SystemModule>> LoadSystemModuleFunc = delegate (int[] rolesid)
            {
                var query = (from mp in _adminrolesystemmoduleReposity.TableNoTracking.Where(o => rolesid.Contains(o.FKAdminRoleId))
                             join m in _sysmoduleRepo.TableNoTracking on mp.FKSystemModuleId equals m.Id into tempinnerjoin
                             from j in tempinnerjoin.DefaultIfEmpty()
                             where j.IsActive == YesorNo.YES && mp.IsAllow == YesorNo.YES
                             orderby j.ParentId, j.DisplayOrder
                             select j).Distinct();
                return query.ToList();
            };
            string cacheKey = string.Format(DefaultCacheConfig.SystemNaviMenuByUser, user.Id);
            var authMenus = _cacheManager.Get(cacheKey, () =>
            {
                var result = new List<SystemModule>();
                foreach (var menu in LoadSystemModuleFunc(userRoles.Select(r => r.Id).ToArray()))
                    result.Add(new MenuForCaching(menu));
                return result;
            });
            return authMenus;
        }
        public virtual SystemModule GetSystemModuleById(int moduleid)
        {
            var result = GetSystemMenusAll().FirstOrDefault(m => m.Id == moduleid);
            if (result == null) return null;
            return result;
        }
        public virtual SystemModule GetSystemModuleByUri(string moduleUri)
        {
            if (string.IsNullOrEmpty(moduleUri)) return null;
            var result = GetSystemMenusAll().FirstOrDefault(m => !string.IsNullOrEmpty(m.UrlRoute) && m.UrlRoute.Equals(moduleUri.ToLowerInvariant()));
            if (result == null) return null;
            return result;
        }
        /// <summary>
        /// Get Mapping List by AdminRole Ids
        /// </summary>
        /// <param name="role"></param>
        /// <returns></returns>
        public virtual IList<AdminRoleSystemModuleMapping> GetAdminRoleSystemModuleMappingsByRoleIds(int[] roleIds)
        {
            if (roleIds == null || roleIds.Length == 0) return null;
            return _adminrolesystemmoduleReposity.TableNoTracking.Where(m => roleIds.Contains(m.FKAdminRoleId)).ToList();
        }
        /// <summary>
        /// 更新用户角色对应系统模块权限
        /// </summary>
        /// <param name="mappings"></param>
        public virtual void UpdateAdminRoleAuthorize(IList<AdminRoleSystemModuleMapping> mappings)
        {
            if (mappings == null || mappings.Count == 0)
                throw new ArgumentNullException(nameof(mappings));
            _adminrolesystemmoduleReposity.Update(mappings);
            string cacheKey = string.Format(DefaultCacheConfig.SystemModuleAuthorizedCacheKey, mappings.First().FKAdminRoleId);
            _cacheManager.Remove(cacheKey);
            cacheKey = DefaultCacheConfig.AdminUserPermissionRecord;
            cacheKey = cacheKey.Substring(0, cacheKey.IndexOf("."));
            _cacheManager.RemoveByPrefix(cacheKey);
            cacheKey = DefaultCacheConfig.AdminUserPermissionButton;
            cacheKey = cacheKey.Substring(0, cacheKey.IndexOf("."));
            _cacheManager.RemoveByPrefix(cacheKey);
            cacheKey = DefaultCacheConfig.SystemNaviMenuByUser;
            cacheKey = cacheKey.Substring(0, cacheKey.IndexOf("-"));
            _cacheManager.RemoveByPrefix(cacheKey);
        }

        public virtual void UpdateSystemModule(SystemModule module)
        {
            if (module == null)
                throw new ArgumentNullException(nameof(module));

            _sysmoduleRepo.Update(module);
            //event notification For Cache
            _eventDispatcher.Dispatch(new EntityUpdatedEvent<SystemModule>(module));
        }
        /// <summary>
        /// Insert a SystemModule entity
        /// </summary>
        /// <param name="module">SystemModule</param>
        public virtual void InsertSystemModule(SystemModule module)
        {
            if (module == null)
                throw new ArgumentNullException(nameof(module));

            _sysmoduleRepo.Insert(module);
            //event notification for Cache
            _eventDispatcher.Dispatch(new EntityInsertedEvent<SystemModule>(module));
        }

        public virtual void DeleteSystemModule(SystemModule module)
        {
            if (module == null)
                throw new ArgumentNullException(nameof(module));
            //需要删除记录，而不是将IsActive置为0,以保证urlroute唯一
            DeleteSystemmoduleRoleMappings(module.Id);
            _sysmoduleRepo.Delete(module);
            //event notification for Cache
            _eventDispatcher.Dispatch(new EntityDeletedEvent<SystemModule>(module));
        }
        protected void DeleteSystemmoduleRoleMappings(int mid)
        {
            //prepare parameters
            var pSystemModuleId = _dataProvider.GetInt32Parameter("ModuleId", mid);

            //invoke stored procedure
            _dbContext.ExecuteSqlCommand("CALL DeleteSystemmoduleRoleMappings(@ModuleId)",
                false, null, pSystemModuleId);
        }

        //public virtual IList<SystemModule> GetModuleButtons(AdminUser user, int moduleId = 0)
        //{
        //    throw new NotImplementedException("framework change for GetModuleButtons");
        //    if (user == null || moduleId <= 0) return null;
        //    string cacheKey = string.Format(DefaultCacheConfig.UserModuleBtns, user.Id, moduleId);
        //    bool isAdmin = user.AdminRoles.Any(u => u.IsAdmin == YesorNo.YES);
        //    Func<int[], int, IList<SystemModule>> LoadSystemModuleBtnsFunc = null;
        //    if (isAdmin)
        //    {
        //        //超级管理员有所有功能显示并操作的权限
        //        LoadSystemModuleBtnsFunc = delegate (int[] rolesid, int moduleId)
        //        {
        //            var query = _sysmoduleRepo.TableNoTracking;
        //            query = query.Where(module => (module.ModuleType == SysModuleType.PageBtn || module.ModuleType == SysModuleType.RecordBtn)
        //                                        && module.IsActive == YesorNo.YES);
        //            query = query.Where(module => module.PID == moduleId);
        //            query = query.OrderBy(module => module.DisplayOrder);
        //            return query.ToList();
        //        };
        //    }
        //    else
        //    {
        //        LoadSystemModuleBtnsFunc = delegate (int[] rolesid, int moduleId)
        //        {
        //            //var query = (from mp in _adminrolesysmoduleMappingRepo.TableNoTracking.Where(o => rolesid.Contains(o.FKAdminRoleId))
        //            //             join m in _sysmoduleRepo.TableNoTracking.Where(m => m.PID == moduleId) on mp.FKSysModuleId equals m.Id into tempinnerjoin
        //            //             from j in tempinnerjoin.DefaultIfEmpty()
        //            //             where (j.ModuleType == SysModuleType.PageBtn || j.ModuleType == SysModuleType.RecordBtn) && j.IsActive == YesorNo.YES
        //            //             orderby j.PID, j.DisplayOrder
        //            //             select j).Distinct();
        //            var query = _sysmoduleRepo.Table.Where(m => m.PID == moduleId && m.IsActive == YesorNo.YES);
        //            query = from mo in query
        //                    join mp in _adminrolesysmoduleMappingRepo.Table.Where(o => rolesid.Contains(o.FKAdminRoleId))
        //                    on mo.Id equals mp.FKSysModuleId into mo_mp
        //                    from sm in mo_mp.DefaultIfEmpty()
        //                    where mo.ModuleType == SysModuleType.PageBtn || mo.ModuleType == SysModuleType.RecordBtn
        //                    select new SystemModule
        //                    {
        //                        Id = mo.Id,
        //                        PID = mo.PID,
        //                        ModuleName = mo.ModuleName,
        //                        ModuleType = mo.ModuleType,
        //                        IconText = mo.IconText,
        //                        IconCssClass = mo.IconCssClass,
        //                        UrlRoute = mo.UrlRoute,
        //                        IsActive = mo.IsActive,
        //                        DisplayOrder = mo.DisplayOrder,
        //                        CreatedOnTimeUtc = mo.CreatedOnTimeUtc,
        //                        OpenOnNewWindow = mo.OpenOnNewWindow,
        //                        IsAllow = sm.IsAllow,
        //                        ButtonEvent = mo.ButtonEvent
        //                    };
        //            return query.ToList();
        //        };
        //    }

        //    var moduleBtns = _cacheManager.Get(cacheKey, () =>
        //    {
        //        var result = new List<SystemModule>();
        //        var roles = user.AdminRoles.Select(m => m.Id).ToArray();
        //        foreach (var button in LoadSystemModuleBtnsFunc(roles, moduleId))
        //            result.Add(new ModuleBtnForCaching(button));
        //        return result;
        //    });

        //    return moduleBtns;
        //}


        #endregion
    }

    /// <summary>
    /// MenuForCaching (for caching)
    /// </summary>
    [Serializable]
    //Entity Framework will assume that any class that inherits from a POCO class that is mapped to a table on the database requires a Discriminator column
    //That's why we have to add [NotMapped] as an attribute of the derived class.
    [NotMapped]
    public class MenuForCaching : SystemModule, IEntityForCaching
    {
        /// <summary>
        /// Ctor
        /// </summary>
        /// <param name="l">SystemModule to copy</param>
        public MenuForCaching(SystemModule m)
        {
            if (m != null)
            {
                Id = m.Id;
                ModuleName = m.ModuleName;
                DisplayName = m.DisplayName;
                ModuleType = m.ModuleType;
                ParentId = m.ParentId;
                IconText = m.IconText;
                IconCssClass = m.IconCssClass;
                UrlRoute = m.UrlRoute;
                IsActive = m.IsActive;
                DisplayOrder = m.DisplayOrder;
                CreatedOnTimeUtc = m.CreatedOnTimeUtc;
                OpenOnNewWindow = m.OpenOnNewWindow;
                ModuleAssignedButtonIds = m.ModuleAssignedButtonIds;
                if (!string.IsNullOrEmpty(ModuleAssignedButtonIds))
                {
                    ModuleAssignedButtons = new List<int>();
                    Type proptype = ModuleAssignedButtons.GetType();
                    if (TypeDescriptor.GetConverter(proptype).CanConvertFrom(typeof(string)))
                        if (TypeDescriptor.GetConverter(proptype).IsValid(ModuleAssignedButtonIds))
                            ModuleAssignedButtons = ModuleAssignedButtons.Concat((List<int>)TypeDescriptor.GetConverter(proptype).ConvertFromInvariantString(ModuleAssignedButtonIds)).ToList();
                        else ModuleAssignedButtons = null;
                    else ModuleAssignedButtons = null;
                }
            }
        }
    }
}
